
using namespace std;

#include "Partida.h"

Partida *Partida::instance = NULL;

/* Mapa de comandos */
map<int, ComandoServidor *> mapaComandos;

Partida *Partida::getInstance(){
    if(!instance){
        instance = new Partida();
        return instance;
    } else {
        return instance;
    }
}

Partida::Partida(){
	//Activo los mutexs.
	this->mutexListaElementos 			= new ThreadMutex();
	this->mutexColaAcciones 			= new ThreadMutex();
	this->mutexColaRespuestaAcciones	= new ThreadMutex();
	this->mutexHayRespuestas			= new ThreadMutex();
	this->mutexHayAcciones				= new ThreadMutex();

	/* Comandos */
	mapaComandos[ACC_MOVER] 	   			= (ComandoServidor *) new ComandoMover();
	mapaComandos[ACC_CREAR_FUEGO]  			= (ComandoServidor *) new ComandoCrearFuego();
	mapaComandos[ACC_CREAR_BARRIL]		= (ComandoServidor *) new ComandoCrearBarril();
	mapaComandos[ACC_CREAR_MARTILLO]			= (ComandoServidor *) new ComandoCrearMartillo();
	mapaComandos[ACC_DELETE_FUEGO] 			= (ComandoServidor *) new ComandoDeleteFuego();
	mapaComandos[ACC_DELETE_BARRIL] 		= (ComandoServidor *) new ComandoDeleteBarril();
	mapaComandos[ACC_DELETE_MARTILLO]			= (ComandoServidor *) new ComandoDeleteMartillo();
	mapaComandos[ACC_LANZAR_BARRIL]		= (ComandoServidor *) new ComandoLanzarBarril();
	mapaComandos[ACC_FIN_TRAMO]				= (ComandoServidor *) new ComandoFinTramo();
	mapaComandos[ACC_FIN_NIVEL]				= (ComandoServidor *) new ComandoFinNivel();
	mapaComandos[ACC_FIN_PARTIDA]			= (ComandoServidor *) new ComandoFinPartida();
	mapaComandos[ACC_SALIR_JUEGO]			= (ComandoServidor *) new ComandoSalirJuego();

	//Arrancan tomados, hasta que se inserten acciones o respuestas.
	this->mutexHayRespuestas->P();
	this->mutexHayAcciones->P();

	/* La partida arranca parada hasta que se puedan ejecutar acciones */
	this->stoped = CONST_TRUE;
	this->finTorneo = CONST_FALSE;
}


void Partida::setFinTorneo(bool finTorneo){
	instance->finTorneo = finTorneo;
}

bool Partida::isFinTorneo(){
	return instance->finTorneo;
}

/* Metodo que inicializa la partida, y envia a los jugadores que numero de jugador son y el nombre de su contrincante */
bool Partida::iniciarPartida(Socket *conexion){
	//Activo el flag de activo.
	instance->activo = true;

	//Inicializo los id de los elementos únicos.
	instance->idDonkey	= CONST_MENOS_UNO;

	/* Envio la accion al cliente. */
	if(!instance->notificarInicioPartida(conexion))
		return CONST_FALSE;

	/* Actualizo el ranking y puntaje de los jugadores desde la shm */
	refreshStatsJugadores(CONST_TRUE, CONST_TRUE);

	//Genero los objetos iniciales vacios y no vivos, para luego sortear por tramo su estado.
	instance->crearElementosIniciales();

	/* Inicializo el primer nivel */
	instance->inicializarNivel(CONST_UNO);

	return CONST_TRUE;
}

/* setea la dificultad del nivel */
void Partida::setParmsPorNivel( short int nivel){
	switch(nivel){
	case 1:
		maxMovSinLanzar			= MV_SIN_LANZ_IN;
		maxCiclosSinFuegos		= CC_SIN_FUEGOS_IN;
		maxCiclosSinTortas		= CC_SIN_TORTAS_IN;
		cantBloqueosAQuitarH	= CONST_UNO;//CN_BQH_QUITAR_IN;
		cantBloqueosAQuitarV	= CONST_UNO;//CN_BQV_QUITAR_IN;
		cantTramosNivelActual 	= CN_TRAMOS_LVL1;
		break;
	case 2:
		maxMovSinLanzar			= MV_SIN_LANZ_IN - DEC_SIN_LANZ;
		maxCiclosSinFuegos		= CC_SIN_FUEGOS_IN - DEC_SIN_FUEGOS;
		maxCiclosSinTortas		= CC_SIN_TORTAS_IN - DEC_SIN_TORTAS;
		cantBloqueosAQuitarH	= CONST_UNO;//(CN_BQH_QUITAR_IN - DEC_BQH_QUITAR)<CONST_UNO?CONST_UNO:(CN_BQH_QUITAR_IN - DEC_BQH_QUITAR);
		cantBloqueosAQuitarV	= CONST_UNO;//(CN_BQV_QUITAR_IN - DEC_BQV_QUITAR)<CONST_UNO?CONST_UNO:(CN_BQV_QUITAR_IN - DEC_BQV_QUITAR);
		cantTramosNivelActual = CN_TRAMOS_LVL2;
		break;
	case 3:
		maxMovSinLanzar			=  maxMovSinLanzar 		- DEC_SIN_LANZ;
		maxCiclosSinFuegos		=  maxCiclosSinFuegos		- DEC_SIN_FUEGOS;
		maxCiclosSinTortas		=  maxCiclosSinTortas	- DEC_SIN_TORTAS;
		cantBloqueosAQuitarH	=  CONST_UNO;//(cantBloqueosAQuitarH- DEC_BQH_QUITAR)<CONST_UNO?CONST_UNO:(cantBloqueosAQuitarH - DEC_BQH_QUITAR);
		cantBloqueosAQuitarV	=  CONST_UNO;//(cantBloqueosAQuitarV - DEC_BQV_QUITAR)<CONST_UNO?CONST_UNO:(cantBloqueosAQuitarV - DEC_BQV_QUITAR);
		cantTramosNivelActual = CN_TRAMOS_LVL3;
		break;
	default:
		maxMovSinLanzar			=  maxMovSinLanzar 		- DEC_SIN_LANZ;
		maxCiclosSinFuegos		=  maxCiclosSinFuegos		- DEC_SIN_FUEGOS;
		maxCiclosSinTortas		=  maxCiclosSinTortas	- DEC_SIN_TORTAS;
		cantBloqueosAQuitarH	=  CONST_UNO;//(cantBloqueosAQuitarH- DEC_BQH_QUITAR)<CONST_UNO?CONST_UNO:(cantBloqueosAQuitarH - DEC_BQH_QUITAR);
		cantBloqueosAQuitarV	=  CONST_UNO;//(cantBloqueosAQuitarV - DEC_BQV_QUITAR)<CONST_UNO?CONST_UNO:(cantBloqueosAQuitarV - DEC_BQV_QUITAR);
		cantTramosNivelActual = CN_TRAMOS_LVL3;
		break;
	}
}

/* Inicializa los objetos del nivel en cuestion como constantes de dificultad y numero de nivel.*/
void Partida::inicializarNivel( short int nivel){
	/* Busco las parametrizaciones de dificultad para este nivel */
	setParmsPorNivel(nivel);

	/* Inicializo el numero de nivel y de tramo inicial. */
	instance->nivelActual		    = nivel;
	instance->tramoActual			= CONST_UNO; 				/* El primer tramo a setear es el 1 */

	/* Encolo la accion para que los jugadores la ejecuten tambien. */
	t_accion action;
	action.id_accion  = ACC_INIT_NIVEL;
	action.id_entidad = nivel;
	instance->encolarRespuestaJugadores(action);

	/* Inicio el primer tramo */
	instance->inicializarTramo(instance->tramoActual);
}

//Inicializa los elementos del tramo, sorteando ya los vidrios rotos y los bloqueos.
void Partida::inicializarTramo(unsigned int tramo){
	/* Inicializo los id de los elementos únicos. */
	instance->idFuego		= CONST_MENOS_UNO;
	instance->idMartillo   = CONST_MENOS_UNO;

	/* Inicializo contadores e indicadores de sentido de desplazamiento. */
	instance->sentidoRalph		  = CONST_UNO;
	instance->cantBarriles		  = CONST_CERO;
	instance->cantVidriosRotos 	  = CONST_CERO;
	instance->cantCiclosSinFuegos	  = CONST_CERO;
	instance->cantCiclosSinTortas = CONST_CERO;

	/* Seteo el numero de tramo */
	instance->tramoActual	   		= tramo;

	/* Reseteo el estado de los elementos que persisten toda la partida o nivel */
	resetObjetosPorTramo();

	/* Por ultimo vuelvo a poner a andar la partida */
	instance->setStoped(CONST_FALSE);
	notificarComenzar(tramo);
}

//Setters
void Partida::setStoped(bool stoped){
	instance->stoped = stoped;
}

void Partida::setShm(SharedMemory *shm){
	instance->shm = shm;
}

void Partida::setSemShm(Semaforo *semShm){
	instance->semShm = semShm;
}

void Partida::setPartida(t_partida *partida){
	instance->partida = *partida;
}

//------------------------------------------------------Notificaciones a los jugadores-----------------------------------------------//

/* Se encarga de notificar a los clientes que comenzo la partida indicandoles que numero de jugador es y el nombre del otro jugador. */
bool Partida::notificarInicioPartida(Socket *conexion){
	/* Luego envio las acciones de comienzo de partida, distintas para cada jugador */
	/* Accion para el jugador1 con el nombre del jugador2 */
	t_accion actionJ1;
	actionJ1.id_accion 	= ACC_INIT_PARTIDA;
	actionJ1.id_entidad	= CONST_ID_MARIO1;
	strcpy(actionJ1.msj, instance->getPartida()->jugador2.nombre);
	/* Accion para el jugador2 con el nombre del jugador1 */
	t_accion actionJ2;
	actionJ2.id_accion 	= ACC_INIT_PARTIDA;
	actionJ2.id_entidad	= CONST_ID_MARIO2;
	strcpy(actionJ2.msj, instance->getPartida()->jugador1.nombre);

	int ret1=0, ret2=0;
	ret1 = conexion->write(((void *)&actionJ1), sizeof(actionJ1), instance->getPartida()->jugador1.socket_id);
	ret2 = conexion->write(((void *)&actionJ2), sizeof(actionJ2), instance->getPartida()->jugador2.socket_id);

	/* Si no se pudo enviar a alguno, se los setea como desconectados del torneo */
	if(ret1 == -1)
		instance->getPartida()->jugador1.conectado = CONST_FALSE;
	if(ret2 == -1 )
		instance->getPartida()->jugador2.conectado = CONST_FALSE;

	/* Cambio el estado de los jugadores en el torneo */
	Partida::getInstance()->actualizarJugadorServidorTorneo();

	/* Si ningun jugador puede jugar, salgo */
	if(ret1 == -1 && ret2 == -1)
		return CONST_FALSE;
	else
		return CONST_TRUE;
}

/* Envia a los jugadores la accion de finalizar partida */
void Partida::notificarFinPartida(){
	t_accion a;
	a.id_accion = ACC_FIN_PARTIDA;
	encolarRespuestaJugadores(a);
}

/* Notifica a los jugadores que se actualizo el estado de los jugadores */
void Partida::notificarActEstadoJugadores(bool puntaje, bool ranking){
	if(puntaje)
		notificarActPuntajeJugadores();

	if(ranking)
		notificarActRankingJugadores();
}

/* Notifica a los jugadores que se actualizo el ranking de los jugadores */
void Partida::notificarActRankingJugadores(){
	/* Encolo la accion para enviarla a los jugadores y que actualicen la informacion */
	t_accion a;

	/* Primero jugador1 */
	a.id_accion  = ACC_ACT_RANK;
	a.id_entidad = CONST_ID_MARIO1;
	a.mov_x		 = instance->partida.jugador1.ranking;
	encolarRespuestaJugadores(a);

	/* Despues jugador2 */
	a.id_accion  = ACC_ACT_RANK;
	a.id_entidad = CONST_ID_MARIO2;
	a.mov_x		 = instance->partida.jugador2.ranking;
	encolarRespuestaJugadores(a);
}

/* Notifica a los jugadores que se actualizo el puntaje de los jugadores */
void Partida::notificarActPuntajeJugadores(){
	/* Encolo la accion para enviarla a los jugadores y que actualicen la informacion */
	t_accion a;

	/* Primero jugador1 */
	a.id_accion  = ACC_ACT_PUNT;
	a.id_entidad = CONST_ID_MARIO1;
	a.mov_x		 = instance->partida.jugador1.puntaje;
	encolarRespuestaJugadores(a);

	/* Despues jugador2 */
	a.id_accion  = ACC_ACT_PUNT;
	a.id_entidad = CONST_ID_MARIO2;
	a.mov_x		 = instance->partida.jugador2.puntaje;
	encolarRespuestaJugadores(a);
}

/* Notifica a los jugadores que comienza un nuevo tramo enviando el numero de tramo y la semilla para el sorteo de vidrios y bloqueos */
void Partida::notificarInicioTramo( short int tramo, int semilla){
	t_accion action;
	action.id_accion  = ACC_INIT_TRAMO;
	action.id_entidad = tramo;
	action.mov_x 	  = semilla;
	instance->encolarRespuestaJugadores(action);
}

/* Notifica a los jugadores que comienza la partida y pueden accionar */
void Partida::notificarComenzar( short int tramo){
	t_accion action;
	action.id_accion  = ACC_COMENZAR;
	action.id_entidad = tramo;
	instance->encolarRespuestaJugadores(action);
}

//-------------------------------------------Utilitarios--------------------------------------------------//

//Agrega la t_accion recibida por parámetro, a la cola de acciones a ejecutar.
void Partida::encolarAccionAProcesar(t_accion action){
	bool liberar;

	if(instance->colaAcciones.empty())
		liberar = true;

	//Encolo la t_accion.
	instance->mutexColaAcciones->P();
	instance->colaAcciones.push(action);
	instance->mutexColaAcciones->V();

	//Si es el primero que cargo, libero el mutex. d
	if(liberar)
		instance->mutexHayAcciones->V();

}

//Va sacando de la cola de acciones, para ejecutarlas.
t_accion Partida::desencolarAccionAProcesar(){
	t_accion a;

	//Espero a que halla acciones encoladas.
	instance->mutexHayAcciones->P();

	//Cuando hay, desencolo.
	instance->mutexColaAcciones->P();
	a = instance->colaAcciones.front();
	instance->colaAcciones.pop();
	instance->mutexColaAcciones->V();

	//Si no quedan mas datos, lo dejo tomado hasta que el que carge la cola lo libere.
	if(!instance->colaAcciones.empty())
		instance->mutexHayAcciones->V();

	return a;
}

/*Va tomando las accciones de la cola, y por cada una busca el comando a ejecutar para atenderla y lo ejecuta */
void Partida::procesarAccion(){
    t_accion action = desencolarAccionAProcesar();
    ComandoServidor *cS = mapaComandos[action.id_accion];
	cS->execute(&action);
}

//Agrega la t_accion recibida por parámetro, a la cola de acciones a ejecutar.
void Partida::encolarRespuestaJugadores(t_accion action){
	bool liberar;

	if(instance->colaRespuestaAcciones.empty())
		liberar = true;

	//Encola la respuesta.
	instance->mutexColaRespuestaAcciones->P();
	colaRespuestaAcciones.push(action);
	instance->mutexColaRespuestaAcciones->V();

	//Si es el primero que cargo, libero el mutex.
	if(liberar)
		instance->mutexHayRespuestas->V();
}

//Va sacando de la cola de acciones, para ejecutarlas.
t_accion Partida::desencolarRespuestaJugadores(){
	t_accion a;
	//Espero a que halla respuestas para desencolar.
	instance->mutexHayRespuestas->P();

	//Cuando hay, desencolo.
	instance->mutexColaRespuestaAcciones->P();
	a = instance->colaRespuestaAcciones.front();
	instance->colaRespuestaAcciones.pop();
	instance->mutexColaRespuestaAcciones->V();

	//Si no quedan mas datos, lo dejo tomado hasta que el que carge la cola lo libere.
	if(!instance->colaRespuestaAcciones.empty())
		instance->mutexHayRespuestas->V();

	return a;
}

/* Actualiza los datos de la partida en la memoria compartida para el servidor de torneo. */
void Partida::actualizarJugadorServidorTorneo(Mario *jugador){
	/* Traigo el puntero a la partida en shm */
	t_partida partidaShm = getPartidaCompartida();

	/* Me fijo que jugador es el que cambio */
	if(jugador->getId()==CONST_ID_MARIO1){
		/* Actualizar el dato partida local para luego impactar el del torneo */
		instance->partida.jugador1.puntaje = jugador->getPuntos();
		/* Actualizo en la shm con torneo */
		partidaShm.jugador1.puntaje  = instance->partida.jugador1.puntaje;
		partidaShm.jugador1.conectado= instance->partida.jugador1.conectado;
	}else{
		/* Actualizar el dato partida local para luego impactar el del torneo */
		instance->partida.jugador2.puntaje = jugador->getPuntos();
		/* Actualizo en la shm con torneo */
		partidaShm.jugador2.puntaje  = instance->partida.jugador2.puntaje;
		partidaShm.jugador2.conectado= instance->partida.jugador2.conectado;
	}

	/* Pisa la shm con el dato modificado */
	actualizarMemoriaCompartida(&partidaShm);
}

/* Actualiza los datos de la partida en la memoria compartida para el servidor de torneo. */
void Partida::actualizarJugadorServidorTorneo(){
	/* Traigo el puntero a la partida en shm */
	t_partida partidaShm = getPartidaCompartida();
	/* Actualizo en la shm con torneo */
	partidaShm.jugador1.puntaje  = instance->partida.jugador1.puntaje;
	partidaShm.jugador1.conectado= instance->partida.jugador1.conectado;
	/* Actualizo en la shm con torneo */
	partidaShm.jugador2.puntaje  = instance->partida.jugador2.puntaje;
	partidaShm.jugador2.conectado= instance->partida.jugador2.conectado;
	/* Pisa la shm con el data modificado */
	actualizarMemoriaCompartida(&partidaShm);
}

/* Trae el puntero a la partida almacenada en shm con el servidor torneo. */
t_partida Partida::getPartidaCompartida(){
	/* Tomo el semaforo para uso exclusivo de la shm */
	instance->semShm->P();

	/* Actualizo el dato en la shm */
	t_partida temp = instance->shm->leerDatos();

	/* Libero el semaforo */
	instance->semShm->V();

	return temp;
}

/* Actualiza los datos de la partida almacenada en shm con el servidor torneo. */
void Partida::actualizarMemoriaCompartida(t_partida *partida){
	/* Tomo el semaforo para uso exclusivo de la shm */
	instance->semShm->P();

	/* Actualizo el dato en la shm */
	instance->shm->escribirDatos(partida);

	/* Libero el semaforo */
	instance->semShm->V();
}

/* Refresca la información de la partida con el ranking que actualiza el servidor torneo en la memoria compartida */
void Partida::refreshStatsJugadores(bool puntaje, bool ranking){
	/* Si pudo actualizar los jugadores desde la memoria compartida, notifico la actualizacion a los jugadores, sino pincha. */
	if(actualizarEstadoJugadores())
		notificarActEstadoJugadores(puntaje, ranking);
	else{
		cout << "[ERROR] - No se puede acceder a la memoria compartida, cerrando servidor partida." << endl;
		exit(0);
	}
}

/* */
bool Partida::actualizarEstadoJugadores(){
	/* Tomo la compartida */
	t_partida partidaShm = instance->getPartidaCompartida();

	/* Actualizo los datos de la partida local desde la memoria compartida */
	instance->partida = partidaShm;

	return CONST_TRUE;
}

//---------------------------------------------Acciones----------------------------------------------------//

//Metodo para la accion ACC_MOVER, y mover el objeto con id:id y en el sentido sentido que la accion lo indique.
int Partida::move(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_CERO;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	//Verifico que se pueda mover la entidad a donde quiere moverse.
	if(sePuedeMover(action))
		instance->mover(action);

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_UNO;
}

/* Verifica si no existen marquesinas o ventanas abiertas para felix. */
bool Partida::sePuedeMover(t_accion *action){
	short int vidrioDestino = 0;
	short int vidrioOrigen = 0;

	//Me posiciono en la entidad seleccionada.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, action->id_entidad);

	if((*it)->puedoMovermeHorizontalmente(action->mov_x) &&
	   (*it)->puedoMovermeVerticalmente(action->mov_y))
		return CONST_TRUE;

	return CONST_FALSE;

}

/* Metodo para realizar el movimiento propiamente dicho */
void Partida::mover(t_accion *action){
	//Me posiciono en la entidad seleccionada.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, action->id_entidad);

	//Muevo la entidad
	if((*it)->getTipo() == TIPO_ELEM_MARIO)
		((Mario *)(*it))->moverse(action->mov_x, action->mov_y);
	else
		(*it)->moverse(action->mov_x, action->mov_y);

	action->mov_x = (*it)->getPosX();
	action->mov_y = (*it)->getPosY();

	//Devuelvo el movimiento al jugador para que lo ejecute.
	instance->encolarRespuestaJugadores(*action);

	//Si hay colisiones con el movimiento y alguno de los involucrados es uno de los felix proceso la colision, sino la ignoro.
	Elemento * colisionado = hayColision((*it));
	if(colisionado && (colisionado->getTipo()== TIPO_ELEM_MARIO || (*it)->getTipo()==TIPO_ELEM_MARIO )){
		procesarColision((*it), colisionado);
	}
}

/* Metodo para eliminar un fuego */
bool Partida::deleteFuego(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo el fuego para ver si esta vivo, solo en ese caso lo borro. */
	Fuego *fuego = (Fuego *)getElementoById(action->id_entidad);

	/* Si el fuego esta viva la borro, sino no hago nada */
	if(fuego->isVivo()){
		/* Le cambio el estado a no vivo */
		fuego->setVivo(CONST_FALSE);

		/* Seteo el id del fuego a nivel partida */
		this->idFuego = CONST_MENOS_UNO;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_FUEGO;
		action.id_entidad = fuego->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Metodo para eliminar una martillo */
bool Partida::deleteMartillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo la torta para ver si esta vivo, solo en ese caso lo borro. */
	Martillo *martillo = (Martillo *)getElementoById(action->id_entidad);

	/* Si el fuego esta viva la borro, sino no hago nada */
	if(martillo->isVivo()){
		/* Le cambio el estado a no vivo */
		martillo->setVivo(CONST_FALSE);

		/* Seteo el id de la torta a nivel partida */
		this->idMartillo = CONST_MENOS_UNO;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_MARTILLO;
		action.id_entidad = martillo->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Metodo para eliminar un barril */
bool Partida::deleteBarril(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	//Tomo el mutex de la lista de elementos a usar.
	this->mutexListaElementos->P();

	/* Traigo el ladrillo para ver si esta vivo, solo en ese caso lo borro. */
	Barril *barril = (Barril *)getElementoById(action->id_entidad);

	/* Si el ladrillo esta vivo la borro, sino no hago nada */
	if(barril->isVivo()){
		/* Le cambio el estado a no vivo */
		barril->setVivo(CONST_FALSE);

		/* Cuento un ladrillo menos */
		instance->cantBarriles--;

		/* Encolo las acciones para devolversela al jugador y que las impacte. */
		t_accion action;

		/* Envio la accion delete. */
		action.id_accion  = ACC_DELETE_BARRIL;
		action.id_entidad = barril->getId();
		instance->encolarRespuestaJugadores(action);
	}

	//Lo libero
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Trae el puntero al elemento del id ingresado, de la lista de elementos de la partida */
Elemento *Partida::getElementoById(int id){
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, id);
	return (*it);
}

void Partida::verFinTramoONivel(){
	/* Si no quedan mas vidrios para reparar  termina el tramo */
	if(cantVidriosRotos == CONST_CERO){
		/* Primero paro la partida hasta que comience el nuevo tramo y/o nivel */
		instance->setStoped(CONST_TRUE);
		/* Despues encolo la accion para terminar el tramo o el nivel si es el ultimo tramo del nivel. */
		crearAccionFinTramoONivel();
	}
}

/* Notifica a los jugadores si ya se termino el tramo y o el nivel, con las acciones correspondientes */
void Partida::crearAccionFinTramoONivel(){
	/* Si el que termino era el ultimo tramo del nivel, encolo la accion de fin de nivel. */
	if(instance->tramoActual == instance->cantTramosNivelActual){
		t_accion actFinNivel;
		actFinNivel.id_accion  = ACC_FIN_NIVEL;
		actFinNivel.id_entidad = instance->nivelActual;
		instance->encolarAccionAProcesar(actFinNivel);
	}else{
		/* Si no termino el nivel, encolo el comando de fin de tramo. */
		t_accion actFinTramo;
		actFinTramo.id_accion  = ACC_FIN_TRAMO;
		actFinTramo.id_entidad = instance->tramoActual;
		instance->encolarAccionAProcesar(actFinTramo);
	}
}

/* Pasa al siguiente tramo */
bool Partida::finalizarTramo(t_accion *action){
	/* Mando a inicializar el siguiente tramo de la partida con el estado de los objetos y demas */
	instance->inicializarTramo((instance->tramoActual + 1));

	return CONST_TRUE;
}

/* Pasa al siguiente nivel. */
bool Partida::finalizarNivel(t_accion *action){
	/* Mando a inicializar el siguiente nivel de la partida */
	instance->inicializarNivel(instance->nivelActual + 1);

	return CONST_TRUE;
}

/* Termina la partida. */
bool Partida::finalizarPartida(t_accion *action){
	/* Paro la partida para dejar de tomar acciones de los jugadores. */
	instance->stoped = CONST_TRUE;

	/* Actualizo por ultima vez el estado de los jugadores desde la shm. */
	refreshStatsJugadores(CONST_TRUE, CONST_TRUE);

	/* Notifico a los jugadores del fin de la partida */
	notificarFinPartida();

	/* Termino por desactivar la partida la partida */
	instance->activo = CONST_FALSE;

	return CONST_TRUE;
}

/* Ejecuta la accion de quitar a un jugador del juego y de la partida */
bool Partida::salirDelJuego(t_accion *action){
	/* Solo puede salir del juego un jugador que este conectado */
	if((action->id_entidad == CONST_ID_MARIO1 && !instance->partida.jugador1.conectado) ||
	   (action->id_entidad == CONST_ID_MARIO2 && !instance->partida.jugador2.conectado)   ){
		cout << "[ERROR] - Se intento desconectar un cliente ya desconectado" << endl;
		return CONST_FALSE;
	}

	/* Me fijo si es el jugador1 o jugador2 y le cambio el estado a desconectado */
	if(action->id_entidad == CONST_ID_MARIO1)
		instance->partida.jugador1.conectado = CONST_FALSE;
	else
		instance->partida.jugador2.conectado = CONST_FALSE;

	/* Busco el felix indicado en la entidad. */
	Mario *mario = (Mario *) getElementoById(action->id_entidad);

	/* Cambio el estado del felix indicado a no vivo */
	mario->setVivo(CONST_FALSE);

	/* Actualizo la info en el servidor torneo */
	actualizarJugadorServidorTorneo(mario);

	/* Encolo la accion para que la ejecuten los jugadores */
	instance->encolarRespuestaJugadores(*action);

	/* Si salieron ambos jugadores termina la partida */
	if(esFinDePartida())
		terminarPartida();

	return CONST_TRUE;
}

/* Chequea que si se dio el fin de partida o no */
bool Partida::esFinDePartida(){
	Mario *mario1 = (Mario *) getElementoById(CONST_ID_MARIO1);
	Mario *mario2 = (Mario *) getElementoById(CONST_ID_MARIO2);

	/* Ver si se desconectaron ambos jugadores */
	if(!instance->partida.jugador1.conectado && !instance->partida.jugador2.conectado)
		return CONST_TRUE;

	/* Ver si murieron los dos jugadores */
	if(!mario2->isVivo() && !mario1->isVivo())
		return CONST_TRUE;

	return CONST_FALSE;
}

/* Si se termino la partida, encolo la accion para procesarla */
void Partida::terminarPartida(){
	t_accion action;
	action.id_accion = ACC_FIN_PARTIDA;
	instance->encolarAccionAProcesar(action);
}

//--------------------------------------------------------------------------------------------------//

/*
 * Método que realiza la animación de los objetos controlados por el programa
 * como ralph, ladrillos, fuegos.
 */
void Partida::animar(){
	this->mutexListaElementos->P();

	instance->eventoAnimarFuego();
	instance->eventoAnimarTorta();
	instance->eventoAnimarRalph();
	instance->eventoAnimarLadrillos();

	this->mutexListaElementos->V();
}

/*
 *Evento que anima a ralph en el techo.
 */
void Partida::eventoAnimarRalph(){
	t_accion action;

	//Me posiciono en ralph en la lista de objetos.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, CONST_ID_DONKEY);
	//Verifico que sea elemento del tipo ralph
	if((*it)->getTipo() == TIPO_ELEM_DONKEY && (*it)->isVivo()){

		//Si no puede realizar el movimiento invierto el sentido. (de right to left y viceversa)
		if(!((*it)->puedoMovermeHorizontalmente((instance->sentidoRalph * ANCHO_MOV_X_RALPH))))
			instance->sentidoRalph = instance->sentidoRalph * CONST_MENOS_UNO;

		//Si encontre a ralph encolo la t_accion de moverse.
		action.id_accion = ACC_MOVER;
		action.id_entidad= (*it)->getId();
		action.mov_x 	 = (instance->sentidoRalph * ANCHO_MOV_X_RALPH);
		action.mov_y 	 = CONST_CERO;

		//Encolo el movimiento de ralph
		instance->encolarAccionAProcesar(action);

		//Veo si ya paso la cantidad de movimientos sin lanzar
		Donkey *donkey = (Donkey *) (*it);
		if(donkey->getCantMovSinLanzar() == instance->maxMovSinLanzar){
			//De ser así lanzo el ladrillo e inicializo el contador.
			instance->crearAccionLanzarBarril(donkey);
		}else{
			//Sino cuento un movimiento mas.
			donkey->addMovSinLanzar();
		}
	}
}

/*
 *Evento que anima a Donkey.
 */
void Partida::eventoAnimarDonkey(){
	t_accion action;

	//Me posiciono en Donkey en la lista de objetos.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, CONST_ID_DONKEY);
	//Verifico que sea elemento del tipo Donkey
	if((*it)->getTipo() == TIPO_ELEM_DONKEY && (*it)->isVivo()){

		//Si encontre a Donkey encolo la t_accion que corresponda.
		action.id_entidad= (*it)->getId();
		action.mov_x 	 = CONST_CERO;
		action.mov_y 	 = CONST_CERO;

		switch((*it)->getIdImagen()) {
		case ACC_DONKEY_ESPERA:
			action.id_accion = ACC_DONKEY_TOMAR;
			break;
		case ACC_DONKEY_TOMAR:
			action.id_accion = ACC_DONKEY_SOSTENER;
			break;
		case ACC_DONKEY_SOSTENER:
			action.id_accion = ACC_DONKEY_LANZAR;
			break;
		default:
			action.id_accion = ACC_DONKEY_ESPERA;
			break;
		}
		(*it)->setIdImagen(action.id_accion);

		//Encolo el movimiento de ralph
		instance->encolarAccionAProcesar(action);

		//Veo si ya paso la cantidad de movimientos sin lanzar
		Donkey *donkey = (Donkey *) (*it);
		if(donkey->getCantMovSinLanzar() == instance->maxMovSinLanzar){
			//De ser así lanzo el ladrillo e inicializo el contador.
			instance->crearAccionLanzarBarril(donkey);
		}else{
			//Sino cuento un movimiento mas.
			donkey->addMovSinLanzar();
		}
	}
}

/*
 *Evento que anima los ladrillos que existen en la partida.
 */
void Partida::eventoAnimarLadrillos(){
 	//Si no ningun ladrillo no los animo.
    if(instance->cantBarriles != CONST_CERO) {
	    bool desaparecer = false;
	    t_accion action;

		//Busco el objeto en la list de objetos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, ID_BASE_BARRIL);
		for (; it != listaElementos.end(); ++it){
			//Por cada ladrillo que encuentro.
    		if((*it)->getTipo() == TIPO_ELEM_BARRIL && (*it)->isVivo()){
    			//Si no puede realizar el movimiento la desaparezco de la pantalla
    			if(!(*it)->puedoMovermeVerticalmente(ANCHO_MOV_Y_OBJ))
    				desaparecer = true;

		    	//Si hay que desaparecer, encolo la t_accion correspondiente
		        if(desaparecer){
		            action.id_accion = ACC_DELETE_BARRIL;
		            action.id_entidad= (*it)->getId();
		        }else {
		        	//Sino encolo la t_accion de moverse.
		            action.id_accion = ACC_MOVER;
		            action.id_entidad= (*it)->getId();
		            action.mov_x 	 = CONST_CERO;
		            action.mov_y 	 = ANCHO_MOV_Y_OBJ;
		        }

		        //Por cada ladrillo encolo la t_accion de moverlo.
		    	instance->encolarAccionAProcesar(action);
    		}
    	}
	}
}

/*
 *Evento que anima las fuegos que existen en la partida.
 */
void Partida::eventoAnimarFuego(){
	//Comienzo viendo si ya existe un fuego.
	if(instance->idFuego != CONST_MENOS_UNO){
		bool desaparecer = false;
    	t_accion action;

		//Me posiciono en el fuego en la lista de objetos.
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, instance->idFuego);
		//Verifico que sea un elemento del tipo fuego y este vivo
		if((*it)->getTipo() == TIPO_ELEM_FUEGO && (*it)->isVivo()){
			Fuego *fuego = (Fuego *) (*it);
			//Si no puede realizar el movimiento la desaparezco de la pantalla
			if(!fuego->puedoMovermeHorizontalmente((ANCHO_MOV_X_OBJ * CONST_MENOS_UNO)))
				desaparecer = true;	

	    	//Si hay que desaparecer, encolo la t_accion correspondiente
	        if(desaparecer){
	            action.id_accion = ACC_DELETE_FUEGO;
	            action.id_entidad= (*it)->getId();
	        }else {
	        	//Sino encolo la t_accion de moverse.
	            action.id_accion = ACC_MOVER;
	            action.id_entidad= (*it)->getId();
	            action.mov_x 	 = (ANCHO_MOV_X_OBJ * CONST_MENOS_UNO);
	            action.mov_y 	 = CONST_CERO;
	        }
	        //Encolo el movimiento o desaparicion.
	    	instance->encolarAccionAProcesar(action);
    	}
	}else{
		//Si no existe un fuego aun, veo si ya pasaron el maximo de ciclos sin fuegos, entonces la creo.
		if(instance->cantCiclosSinFuegos == instance->maxCiclosSinFuegos){
			instance->crearAccionFuego();
		}else{
			//Sino cuento un ciclo mas sin fuegos.
			instance->cantCiclosSinFuegos++;
		}
	}
}


/*
 *Evento que anima las tortas que existen en la partida.
 */
void Partida::eventoAnimarTorta(){
	/* Comienzo viendo si ya existe una torta */
	if(instance->idMartillo == CONST_MENOS_UNO){
		/* Si no existe una torta aun, veo si ya pasaron el maximo de ciclos sin tortas, entonces la creo. */
		if(instance->cantCiclosSinTortas == instance->maxCiclosSinTortas){
			instance->crearAccionTorta();
		}else{
			/* Sino cuento un ciclo mas sin fuegos. */
			instance->cantCiclosSinTortas++;
		}
	}
}

/*
 * Hace que donkey recibido por parámetro, lance un barril.
 * Este método se divide en dos acciones, una sería crear el barril en la pos x e y, donde termina donkey
 * y la otra es que donkey haga el movimiento de lanzar barril.
 * Ambas se enconlan para ser procesadas por el consumidor.
 */
void Partida::crearAccionLanzarBarril(Donkey *donkey){
	//Solo voy a lanzar barril, cuando halla menos de 5 en pantalla, es el maximo.
	if(instance->cantBarriles < MAX_CANT_BARRILES){
		t_accion accionLanzar;
		t_accion accionCrear;

		//Primero mando a donkey a hacer los movimientos de lanzar.
		accionLanzar.id_accion = ACC_LANZAR_BARRIL;
		accionLanzar.id_entidad= donkey->getId();
		instance->encolarAccionAProcesar(accionLanzar);

		//Despues mando a crear el barril a los pies de donkey, en la mitad del ancho.
		accionCrear.id_accion = ACC_CREAR_BARRIL;
		accionCrear.mov_x 	  = donkey->getPosX() + ((donkey->getAncho()/2)-35);	//Arranca justo en la mitad del cuerpo de donkey.
		accionCrear.mov_y 	  = donkey->getPosY() + donkey->getAlto();		//Arranca en los pies de donkey.
		instance->encolarAccionAProcesar(accionCrear);
	}
}

/*
 * Encola el comando de crear un fuego, cuando no existe y ya se cumplio el tiempo de espera.
 * la posición (x,y) del fuego, se definen aleatoriamente.
 */
void Partida::crearAccionFuego(){
	t_accion accionCrear;

	//Defino la posición inicial del fuego, a elegir entre los tres estamentos de ventana existentes.
    srand(time(NULL));
    int x = LIM_DER;
    int y = POS_INI_Y_FUEGO - ((rand() % CONST_TRES) * ANCHO_MOV_Y_FUEGO);

	//Despues envio el comando para crear el fuego en el x,y seleccionados.
	accionCrear.id_accion	= ACC_CREAR_FUEGO;
	accionCrear.id_entidad	= CONST_ID_FUEGO;
	accionCrear.mov_x 		= x;
	accionCrear.mov_y 		= y;
   	instance->encolarAccionAProcesar(accionCrear);
}


/*
 * Encola el comando de crear una torta, cuando no existe y ya se cumplio el tiempo de espera.
 * El vidrio de la torta, se definen aleatoriamente.
 */
void Partida::crearAccionTorta(){
	t_accion accionCrear;

	//Defino la posición inicial de la torta, elige en cual de las 15 ventanas va a estar.
    srand(time(NULL));
    int posVidrio = (rand() % CONST_CANT_VIDRIOS);

    if((posVidrio >= 5 && posVidrio <= 9)   ||
       (posVidrio >= 15 && posVidrio <= 19) ||
       (posVidrio >= 25 && posVidrio <= 30)   )
    	posVidrio = posVidrio - 5;

	//Despues envio el comando para crear el fuego en el x,y seleccionados.
	accionCrear.id_accion	= ACC_CREAR_MARTILLO;
	accionCrear.id_entidad	= CONST_ID_MARTILLO;
	accionCrear.mov_x 		= posVidrio;
   	instance->encolarAccionAProcesar(accionCrear);
}

//Crea los elementos iniciales vacios sin vida.
void Partida::crearElementosIniciales(){
	//Tomo el mutex de la lista de elementos a modificar.
	this->mutexListaElementos->P();

	//Creo los objetos iniciales necesarios para la partida.
	//Donkey
	crearDonkey();
	//Felix1
	crearMario(CONST_UNO);
	//Felix2
	crearMario(CONST_DOS);
	//Fuego inicial no viva.
	crearFuegoInicial();
	//Torta inicial no viva.
	crearMartilloInicial();
	//Stock de ladrillos iniciales no vivos.
	crearBarrilesIniciales();

	//La libero
	this->mutexListaElementos->V();
}

//Accion que crea un fuego.
void Partida::crearMario(int nroMario){
	short int id;
	int posX;
	int posY;
	long int puntos;

	//Veo si estoy creando al mario1 o al mario2.
	if(nroMario == CONST_UNO){
		id 		  = CONST_ID_MARIO1;
		posX	  = POS_INI_X_MARIO1;
		posY	  = POS_INI_Y_MARIO1;
		puntos	  = instance->partida.jugador1.puntaje;
	}else{
		id 		  = CONST_ID_MARIO2;
		posX	  = POS_INI_X_MARIO2;
		posY	  = POS_INI_Y_MARIO2;
		puntos	  = instance->partida.jugador2.puntaje;
	}

	//Creo la instancia del felix correspondiente y lo inserto.
	Mario *mario = new Mario(id, CONST_CERO, posX, posY, ALTO_MARIO, ANCHO_MARIO, TIPO_ELEM_MARIO, CONST_TRUE, VIDAS_POR_DEFECTO, puntos);
	instance->listaElementos.push_back((Elemento *)mario);
}

//Accion que crea un fuego.
void Partida::crearDonkey(){
	//Creo Ralph en la posición inicial por defecto y seteo el idRalph en el singleton para accederlo mas rápido.
	Donkey *donkey = new Donkey(CONST_ID_DONKEY, CONST_CERO, POS_INI_X_DONKEY, POS_INI_Y_DONKEY, ALTO_DONKEY,ANCHO_DONKEY, TIPO_ELEM_DONKEY, CONST_TRUE);
	instance->listaElementos.push_back((Elemento *)donkey);

	//Seteo el id de ralph para no buscarlo y acceder directamente.
	instance->idDonkey = CONST_ID_DONKEY;
}

//Crea el elemento fuego inicial, pero en estado no vivo, cuando quiere aparecer un fuego pasa a vivo = true;
void Partida::crearFuegoInicial(){
	//Creo el Fuego en el indice por defecto 55 y con vivo = false, para que se active recien cuando se anima por el juego mismo.
	Fuego *fuego = new Fuego(CONST_ID_FUEGO, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_FUEGO,
					   ANCHO_FUEGO, TIPO_ELEM_FUEGO, CONST_FALSE);
	instance->listaElementos.push_back((Elemento *)fuego);
}

void Partida::crearMartilloInicial(){
	//Creo el Fuego en el indice por defecto 56 y con vivo = false, para que se active recien cuando se anima por el juego mismo.
	Martillo * martillo = new Martillo(CONST_ID_MARTILLO, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_MARTILLO, ANCHO_MARTILLO, TIPO_ELEM_MARTILLO, CONST_FALSE);
	instance->listaElementos.push_back((Elemento *)martillo);
}

void Partida::crearBarrilesIniciales(){
	//Creo el stock de ladrillos no vivos, para que se vallan activando a medida que sean lanzados.
	for(int index=CONST_CERO; index<MAX_CANT_BARRILES; index++){
		//Calculo el id del ladrillo, tomando el id base que parte del ladrillo 0 y sumandole el indice del ciclo de creación.
		 short int idBarril = ID_BASE_BARRIL + index;
		//Lo creo y lo inserto en la lista de elementos.
		Barril *b = new Barril(idBarril, CONST_CERO, CONST_CERO, CONST_CERO, ALTO_BARRIL,
				ANCHO_BARRIL, TIPO_ELEM_BARRIL, CONST_FALSE);
		instance->listaElementos.push_back((Elemento *)b);
	}
}

//--------------------------------------------------------------------------------------------------------//

//												Getters													  //
t_partida *Partida::getPartida(){
	return &(this->partida);
}

t_jugador *Partida::getJugadorXNro( short int nroJugador){
	if(nroJugador == CONST_UNO)
		return &(this->partida.jugador1);
	else
		return &(this->partida.jugador2);
}

bool Partida::isStoped(){
	return instance->stoped;
}

bool Partida::isActivo(){
	return activo;
}

//--------------------------------------------------------------------------------------------------------//
/* Verifica si el elemento recien movido, no colisiono contra algo, si es asi devuelve contra que. */
Elemento *Partida::hayColision(Elemento *enMovimiento){

	//Recorro la lista de objetos viendo si colisiona con alguno de los vivos. solo veo fuegos, ladrillos o tortas, el resto no.
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, CONST_ID_MARIO1);
	for (; it != listaElementos.end(); ++it){
		//La comparacion tiene que ser contra todos, menos contra si mismo.
		if((*it)->getId() != enMovimiento->getId() && (*it)->isVivo())
		{
			Elemento *elem = (*it);
			if(	( (enMovimiento->getPosX() > elem ->getPosX()) &&
				  (enMovimiento->getPosX() < (elem ->getPosX() + elem ->getAncho())) &&
				  (enMovimiento->getPosY() > elem ->getPosY()) &&
				  (enMovimiento->getPosY() < (elem ->getPosY() + elem ->getAlto())) )
				 ||
				( ((enMovimiento->getPosX() + elem ->getAncho()) > elem ->getPosX()) &&
				  ((enMovimiento->getPosX() + elem ->getAncho()) < (elem ->getPosX() + elem ->getAncho())) &&
				  ((enMovimiento->getPosY() + elem ->getAlto()) > elem ->getPosY()) &&
				  ((enMovimiento->getPosY() + elem ->getAlto()) < (elem ->getPosY() + elem ->getAlto())) )		)
				return elem;
		}
	}

	return NULL;
}

/*
 * Realiza el impacto necesario sobre los felix cuando son colisionados.
 */
void Partida::procesarColision(Elemento *colisionador, Elemento *colisionado){
	Mario	 *mario    = NULL;
	Elemento *elemento = NULL;

	//Me fijo cual de los dos es felix, porque es quien se ve impactado el otro elemnto sigue como si nada.
	if(colisionador->getTipo()==TIPO_ELEM_MARIO){
		mario 	 = (Mario *)colisionador;
		elemento = colisionado;
	}else{
		mario 	 = (Mario *)colisionado;
		elemento = colisionador;
	}
	//Si no es inmune lo colisiono.
	if(!mario->isInmune()){
		colisionar(mario, elemento);
	}
}

/**
 * Ejecuta la colision de uno de los mario contra cualquier objeto.
 */
void Partida::colisionar(Mario *mario, Elemento *elemento){
	/* Si es una torta, la accion a ejecutar es volver inmune a mario y eliminar la martillo. */
	if(elemento->getTipo()==TIPO_ELEM_MARTILLO){
		consumirMartillo(mario, ((Martillo *)elemento));
	}else{
		/* Sino Le resto la vida a mario */
		mario->restarVida();

		/* Encolo la accion para devolversela al jugador y que la impacte */
		t_accion action;
		action.id_accion  = ACC_RESTAR_VIDA;
		action.id_entidad = mario->getId();
		instance->encolarRespuestaJugadores(action);

		/* Si murio con esta colision, envio la accion al jugador de morir este mario */
		if(!mario->isVivo()){
			/* Encolo la accion de morir */
			action.id_accion  = ACC_MORIR;
			action.id_entidad = mario->getId();
			instance->encolarRespuestaJugadores(action);

			/* Si salieron ambos jugadores termina la partida */
			if(esFinDePartida())
				terminarPartida();
		}
	}
}

/*
 * Hace que felix consuma la torta, se vuelva inmune y desaparezca la torta de la pantalla.
 */
void Partida::consumirMartillo(Mario *mario, Martillo *martillo){
	/* Vuelvo inmune a felix , le sumo los puntos de la torta y mato la torta*/
	mario->setInmune(CONST_TRUE);
	mario->sumarPuntos(PTOS_CONS_TORTA);
	martillo->setVivo(CONST_FALSE);

	/* Encolo las acciones para devolversela al jugador y que las impacte
	 * se generan 3 distintas, inmunizar a felix, actualizar puntaje del jugador y desaparecer la torta.
	 */
	t_accion action;

	/* Eliminar torta */
	action.id_accion  = ACC_DELETE_MARTILLO;
	action.id_entidad = martillo->getId();
	//action.mov_x 	  = martillo->getPosVidrio();
	instance->encolarRespuestaJugadores(action);

	/* Inmunizar */
	action.id_accion  = ACC_CONSUMIR_TORTA;
	action.id_entidad = mario->getId();
	instance->encolarRespuestaJugadores(action);

	/* Actualizar puntaje */
	action.id_accion  = ACC_ACT_PUNT;
	action.id_entidad = mario->getId();
	action.mov_x	  = mario->getPuntos();
	instance->encolarRespuestaJugadores(action);
	actualizarJugadorServidorTorneo(mario);
}

/* Metodo que interpreta la accion crear fuego y activa un fuego en la pos x,y indicada en la accion */
bool Partida::crearFuego(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Tomo el mutex de la lista de elementos a modificar */
	this->mutexListaElementos->P();

	/* Traigo el fuego de la lista de elementos */
	Fuego *fuego = (Fuego *) getElementoById(action->id_entidad);

	/* Revivo el fuego, cambiandole la posicion a donde me lo indica la accion */
	fuego->setVivo(CONST_TRUE);
	fuego->setPosX(action->mov_x);
	fuego->setPosY(action->mov_y);

	/* Seteo el id del fuego para que sepan que esta creada y que en este ciclo se creo un fuego. */
	instance->idFuego = CONST_ID_FUEGO;
	instance->cantCiclosSinFuegos = CONST_CERO;

	/* Envio la misma accion a los jugadores */
	t_accion a;
	a.id_accion	 = ACC_CREAR_FUEGO;
	a.id_entidad = fuego->getId();
	a.mov_x		 = action->mov_x;
	a.mov_y		 = action->mov_y;
	encolarRespuestaJugadores(a);

	/* Lo libero */
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Metodo que interpreta la accion crear fuego y activa un fuego en la pos x,y indicada en la accion */
bool Partida::crearMartillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Traigo la torta de la lista de elementos */
	Martillo *martillo = (Martillo *) getElementoById(action->id_entidad);

	/* Revivo la torta, cambiandole el vidrio a donde me lo indica la accion */
	martillo->setVivo(CONST_TRUE);

	/* Seteo el id de la torta para que sepan que esta creada y que en este ciclo se creo una torta. */
	instance->idMartillo = CONST_ID_MARTILLO;
	instance->cantCiclosSinTortas = CONST_CERO;

	/* Envio la misma accion a los jugadores */
	t_accion a;
	a.id_accion	 = ACC_CREAR_MARTILLO;
	a.id_entidad = martillo->getId();
	a.mov_x		 = action->mov_x;
	a.mov_y		 = 0;
	encolarRespuestaJugadores(a);

	return CONST_TRUE;
}


/* Metodo que interpreta la accion crear barril */
bool Partida::crearLadrillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Tomo el mutex de la lista de elementos a modificar */
	this->mutexListaElementos->P();

	/* Si todavia hay lugar para un ladrillo mas */
	if(instance->cantBarriles < MAX_CANT_BARRILES){

		/* Busco el primer barril de la lista que no este vivo,partiendo del Id base de los barriles. */
		std::list<Elemento *>::iterator it = listaElementos.begin();
		std::advance(it, ID_BASE_BARRIL);
		for (; it != listaElementos.end(); ++it){

			/* Por cada barril que encuentro si no esta vivo, lo reutilizo. */
    		if((*it)->getTipo() == TIPO_ELEM_BARRIL && !((*it)->isVivo()) ){

    			/* Revivo el barril */
    			reutilizarBarril(((Barril *)(*it)), action->mov_x, action->mov_y);

    			/* Envio la misma accion a los jugadores */
    			t_accion a;
    			a.id_accion	 = ACC_CREAR_BARRIL;
    			a.id_entidad = (*it)->getId();
    			a.mov_x		 = action->mov_x;
    			a.mov_y		 = action->mov_y;
    			encolarRespuestaJugadores(a);

    			/* Salgo del ciclo */
    			break;
    		}
		}
	}

	/* Lo libero */
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Revive un ladrilo y lo posiciona en los x,y ingresados por parametro. */
void Partida::reutilizarBarril(Barril *barril, int posX, int posY){
	/* Activo el ladrillo pasandolo a vivo cambiandole la posicion a donde me lo indica la accion */
	barril->setVivo(CONST_TRUE);
	barril->setPosX(posX);
	barril->setPosY(posY);

	/* Incremento la cantidad de ladrillos en pantalla para evitar pasar el maximo. */
	instance->cantBarriles++;
}

/* Hace que ralph se mueva como lanzando un ladrillo */
bool Partida::lanzarLadrillo(t_accion *action){
	/* Solo procesa si no esta parado el server */
	if(instance->isStoped())
		return CONST_FALSE;

	/* Tomo el mutex de la lista de elementos a modificar */
	this->mutexListaElementos->P();

	/* Traigo la torta de la lista de elementos */
	Donkey *donkey = (Donkey *) getElementoById(action->id_entidad);

	/* Si esta vivo envio a los jugadores la orden de animar a ralph moviendo un ladrillo. */
	if(donkey->isVivo()){
		/* Seteo en cero la cantidad de movimientos sin lanzar ladrillos de ralph. */
		donkey->setCantMovSinLanzar(CONST_CERO);

		/* Envio la misma accion a los jugadores */
		t_accion a;
		a.id_accion	 = ACC_LANZAR_BARRIL;
		a.id_entidad = donkey->getId();
		a.mov_x		 = action->mov_x;
		a.mov_y		 = action->mov_y;
		encolarRespuestaJugadores(a);
	}

	/* Lo libero  */
	this->mutexListaElementos->V();

	return CONST_TRUE;
}

/* Resetea todos los elementos que son persistentes dentro de la partida o nivel y no se crean en el tramo */
void Partida::resetObjetosPorTramo(){
	/* Tomo el mutex de la lista de elementos a modificar */
	this->mutexListaElementos->P();

	resetDonkey();
	resetMarios();
	resetBarriles();
	resetFuego();
	resetMartillo();

	/* Lo libero */
	this->mutexListaElementos->V();
}

/* Inicializa ralph a la posicion inicial del tramo y su cantidad de movimientos sin lanzar */
void Partida::resetDonkey(){
	/* Busco a ralph dentro de la lista de elementos */
	Donkey *donkey = (Donkey *)getElementoById(CONST_ID_DONKEY);

	/* Ahora lo ubico en las posiciones x,y de inicio de ralph y pongo en cero el contador para lanzar */
	donkey->setPosX(POS_INI_X_DONKEY);
	donkey->setPosY(POS_INI_Y_DONKEY);
	donkey->setCantMovSinLanzar(CONST_CERO);

	/* Piso el nombre por si no estaba */
	instance->idDonkey = CONST_ID_DONKEY;
}

/* Inicializa los mario1 y mario2 a la posicion inicial y los desinmuniza */
void Partida::resetMarios(){
	/* Busco a mario 1 y mario 2*/
	Mario *mario1 = (Mario *)getElementoById(CONST_ID_MARIO1);
	Mario *mario2 = (Mario *)getElementoById(CONST_ID_MARIO2);

	/* Ahora lo ubico en las posiciones x,y y de vidrio de inicio de cada uno*/
	mario1->setPosX(POS_INI_X_MARIO1);
	mario1->setPosY(POS_INI_Y_MARIO1);

	mario2->setPosX(POS_INI_X_MARIO2);
	mario2->setPosY(POS_INI_Y_MARIO2);

	/* Los desinmunizo por si estaban inmunizados */
	mario1->setInmune(CONST_FALSE);
	mario2->setInmune(CONST_FALSE);
}

/* Elimina todos los barriles vivos para arrancar sin barriles el tramo */
void Partida::resetBarriles(){
	/* Recorro todos los barriles */
	std::list<Elemento *>::iterator it = listaElementos.begin();
	std::advance(it, ID_BASE_BARRIL);
	for (; it != listaElementos.end(); ++it){
		/* Por cada ladrillo que encuentro. */
		if((*it)->getTipo() == TIPO_ELEM_BARRIL){
			/* Lo pongo como no vivo, para que no se vea */
			(*it)->setVivo(CONST_FALSE);
		}
	}

	/* Por si acaso seteo en cero la cantidad de ladrillos */
	instance->cantBarriles = CONST_CERO;
}

/* Elimina el fuego si existe en el juego para iniciar sin fuegos el tramo */
void Partida::resetFuego(){
	/* Busco a ralph dentro de la lista de elementos */
	Fuego *fuego = (Fuego *)getElementoById(CONST_ID_FUEGO);

	/* Ahora le cambio el estado a no viva */
	fuego->setVivo(CONST_FALSE);

	/* Piso el nombre por si estaba */
	instance->idFuego = CONST_MENOS_UNO;
}

/* Elimina la torta si existe en el juego para iniciar sin tortas el tramo */
void Partida::resetMartillo(){
	/* Busco a ralph dentro de la lista de elementos */
	Martillo *martillo = (Martillo *)getElementoById(CONST_ID_MARTILLO);

	/* Ahora le cambio el estado a no viva */
	martillo->setVivo(CONST_FALSE);

	/* Piso el nombre por si estaba */
	instance->idMartillo = CONST_MENOS_UNO;
}

